require 'iconv'

class TaskController < ApplicationController
  before_filter :login_required
  helper_method :get_state_count
  helper TaskHelper
  
  #auto_complete_for :user, :name
  #auto_complete_for :project, :name

  STATES =
   [['Inbox','inbox'],
    ['Next Action','next'],
    ['Waiting','waiting'],
    ['Sometime/Maybe','maybe'],
    ['Reference','ref']]
	# and 'folder'

  def index
    id = params[:id]
    session[:selected_project] = id if id != nil
    session[:selected_special] = '0' if not session[:selected_special]
    @tasks,@tags,@project_id = select_tasks
    flash[:notice] = "You can drag tasks to reorder them" if @tasks.size > 0
    flash[:members] = get_project_resp(@project_id)
    @resp = find_responsibles
    @state_count = get_state_count
    @projects =  User.find(session['user']).active_projects
    #get_deadlines
    get_ost
  end

  def do_select_tasks(user_id, project_id, resp_id, tags='', state='0', sel_task='', special=nil)
    logger.debug("===== SELECT TASKS: user=#{user_id}, project_id=#{project_id}, resp_id=#{resp_id}, tags=#{tags}, state=#{state}, sel_task=#{sel_task}, flat=#{special}")
    user = User.find(user_id)
    cond = "(progress=#{PROGRESS_TODO} or progress=#{PROGRESS_BEGUN})"
    user_projects = user.active_projects_ids.join(',')
    if user_projects != ''
      if project_id.to_i != 0
        cond += " and project_id='#{project_id}'"
      else
        cond += " and project_id in (#{user_projects})"
      end
      if resp_id.to_i != 0
        cond += " and (user_id='#{resp_id}' or state='folder')"
      end  
    else
      cond += " and user_id='#{user.id}'"
    end
    #if(flat == nil) # always display folders
    #  cond  += " or state='folder'"
    #end
    if(state != '0' and state != 'all')
      if(state[0].chr != '!')
        cond += " and (state='#{state}' or state='folder')"  
      else
        cond += " and state!='#{state[1..-1]}' and state!='folder'"
      end
    end
    
    flat = nil
    if(special !=nil and special != '0' and special != 'all')
      flat = true # if something special, then it is always flat
      case special
        when 'starred'
          cond += " and star=1"
        when 'begun'
          cond += " and progress=1"
        when 'west'
          cond += " and orig_est is not null"
        when 'woest'
          cond += " and orig_est is null"
        when 'wprio'
          cond += " and priority is not null"
        when 'flat'
          #cond += " and orig_est is null"
      end
    end
    
    if(flat==nil and sel_task != '' and tags == '') # and tags != '' is dangerous if we forget that sel_task will then not be used when displaying other states
      cond += " and task_id='#{sel_task}'"
    end  
    
    order = "`order`"
    inc = ["user","tags","project"]
    if tags != ''
      # tag display and normal display is different: task_id is not filtered meaning that tree structure is not respected
      tasks = Task.find_tagged_with(tags, {:conditions=>cond, :include=>inc, :order=>order})
    else
      tasks = Task.find(:all, :conditions=>cond, :include=>inc, :order=>order)
    end
    
    logger.debug("===== cond=#{cond}")
    logger.debug("===== tasks.size=#{tasks.size}")

    # gives only the tags of the current displayed tasks
    tags = tasks.inject([]) {|tags,t| tags += t.tags}.uniq.sort_by{|t| t.name.downcase}
    [tasks,tags,project_id,resp_id,state]
  end  

  # select user's tasks with filters
  # * session[:selected_project]
  # * session[:selected_resp]
  # * session[:selected_state]
  # * tags
  # return [tasks,tags,project_id,user_id,state]
  def select_tasks(tags='', state='', sel_task='')
    project_id  = (session[:selected_project] || 0).to_i
    resp_id     = (session[:selected_resp] || 0).to_i
    special     = session[:selected_special] || nil
    sel_task    = session[:selected_task] || '' if sel_task == ''
    if(state=='all') # does not take into account the state
      state = '0'
    elsif state=='' # we take the session value
      state = (session[:selected_state] || "0")
    end  
    do_select_tasks(session['user'],project_id,resp_id,tags,state,sel_task, special)
  end

  def get_ost # "ost: other state tasks: tasks with state that does not match the current filter"
    logger.debug('get_ost')
    # FIXME : overkill ? we get the number of ost only
    @ost = do_select_tasks(session['user'],session[:selected_project],session[:selected_resp],'',"!#{session[:selected_state]}",session[:selected_task], session[:selected_special])[0]
    #@ost = sort_tasks(@ost) # useless, since we get the number of ost only
  end  

  # Partial
  def display_tasks_done
    @project_id = (session[:selected_project] || 0).to_i
    @userid = (session[:selected_resp] || 0).to_i
    user_projects = User.find(session['user']).active_projects_ids.join(',')
    cond = "(progress=#{PROGRESS_DONE} or progress=#{PROGRESS_GIVENUP})"
    if(user_projects != '')
      if @project_id != 0
        cond += " and project_id='#{@project_id}'"
      else
        cond += " and project_id in (#{user_projects})"
      end
      cond += " and user_id='#{@userid}'" if @userid != 0
    else
      cond += "and user_id='#{session['user']}'"
    end
    
    @tasks = Task.find(:all, :conditions=>cond, :limit=> 100, :order=>"`done_on` desc")
    render(:partial => 'tasksdone')
  end

  def disp_sub
    id = params[:id]
    if id
      session[:selected_task] = id
      session[:selected_project] = Task.find(id).project_id if id != '0'
      if params['cp'] == '1'
        render(:text=>"window.location='/task'")
        return
      end
    end
    @tasks,@tags,@project_id = select_tasks
    #get_deadlines
    get_ost
    #render(:partial=>'taskcontent', :locals=>{:by_tag=>nil})
    render(:text=>"new Ajax.Updater('taskcontent', '/task/refresh_task_content', {asynchronous:true, evalScripts:true, onLoading:function(request){$('loading').show();}}); return false;")
  end

  def goto_bm
    id = params[:id]
    if id
      begin
        task = Task.find(id)
      rescue
        # happens if the task is deleted
        # The bookmark is destroyed in DB but it is still displayed and we can click on it
        render(:text=>"alert('This task does not exist anymore')")
        return
      end
      session[:selected_task] = id
      session[:selected_project] = task.project_id
      if params['cp'] == '1'
        render(:text=>"window.location='/task'")
        return
      end
    end
    @tasks,@tags,@project_id = select_tasks
    #get_deadlines
    get_ost
    render(:text=>"new Ajax.Updater('taskcontent', '/task/refresh_task_content', {asynchronous:true, evalScripts:true, onLoading:function(request){$('loading').show();}}); return false;")
  end


  def expand_task
    t         = Task.find(params[:id])
    item_name = params[:item_name]
    #@tasks,@tags,@project_id = select_tasks
    @tasks = sort_tasks(t.subtasks)
    render(:partial=>'task', :collection=>@tasks, :locals=>{:item_name=>item_name})#, :locals=>{:task_c=>t, :tasks=>@tasks, :expand=>true, :item_name=>item_name})
  end
  
  def filter_by_tag
    tags  =  params[:t]
    session[:selected_task] = '0'
    @tasks,@tags,@project_id = select_tasks(tags)
    #get_deadlines
    get_ost
    render(:partial=>'taskcontent', :locals=>{:by_tag=>(tags=='' ? nil:true)})
  end
  
  def edit
    id = params[:id].to_i
    return if not verify_task_rights(id)
    begin
      t = Task.find(id)
      render(:partial => 'task_edit_c', :locals=>{:task=>t, :user=>t.user, :project=>t.project, :item_name=>params[:item_name], :is_my_task=>t.user_id==session['user'].to_i})
    rescue Exception => e
      render(:text=>e.message)
    end 
  end

  def get_deadlines
    cond = "done='0' and duedate<= '"+(Date.today+8).to_s + "'"
    if session[:selected_project].to_i != 0
      cond += " and project_id=#{session[:selected_project]}"
    else
      ids = User.find(session['user']).active_projects_ids
      if ids == []
        cond += " and user_id='#{session['user']}'"
      else
        cond += " and project_id in (#{ids.join(',')})"
      end  
    end  
    @deadlines= Task.find(:all, :conditions=>cond, :include=>["user","tags"],:order=>"duedate,priority,`order`")
  end  

  def display_deadlines
    get_deadlines
    render(:partial=>'task/deadlines')  
  end

  def refresh_deadlines
    get_deadlines
    render(:partial=>'task/deadline_tasks')  
  end

  def find_responsibles(project_id=0)
    u = User.find(session['user'])
    if(project_id!=0)
      begin; @resp = Project.find(project_id).users; rescue; @resp = []; end
    else
      @resp = u.coworkers
      @resp = @resp.sort_by {|u| u.name}
    end
  end

  def esc_js(js)
   (js || '').gsub(/(\r\n|\n|\r)+/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
  end

  # Ajaxed
  def createnew
    t             = Task.new
    t.user_id     = session['user'].to_i
    t.created_by  = session['user'].to_i
    t.project_id  = (session[:selected_project] || 0).to_i
    t.order       = 0
    t.task_id     = (session[:selected_task] || 0).to_i # the new task is the sibling of displayed tasks
    t.save
    inner = esc_js(render_to_string(:partial => "task_edit", :locals=>{:task=>t, :item_name=>'item', :is_my_task=>true}))
    render(:text=> "displaynew(#{t.id},'#{inner}')")
  end

  def quick_add
    title = params['t']
    t             = Task.new
    t.user_id     = session['user'].to_i
    #t.project_id = (session[:selected_project] || 0).to_i
    t.order       = 0
    t.title       = title
    t.save
    render(:text=>"<li>#{title}</li>")
  end

  # Ajaxed
  def add_sub
    parent_id     = params[:id].to_i
    
    # verify that the task is not deleted (could happen when we have ;ultiple views on one page)
    begin
      parent = Task.find(parent_id)
    rescue
      render(:text=>'Parent task already deleted')
      return      
    end

    item_name = params[:item_name]
    render(:text=> "alert('no item_name on add_sub');") and return if item_name == ''
    t             = Task.new
    t.user_id     = session['user'].to_i
    t.project_id  = parent.project_id
    t.state       = parent.state
    t.order       = 0 # relative to sub tasks
    t.task_id     = parent_id
    t.save
    render(:partial => 'task_edit', :locals=>{:task=>t, :user=>t.user, :project=>t.project, :item_name=>item_name, :is_my_task=>true})
  end

  # Ajaxed
  def save # save a edited task
    item_name = params[:item_name]
    render(:text=> "alert('no item_name on save');") and return if item_name == ''
    t_id  = params[:id]
    t     = Task.find(t_id)
    render(:text=> "$('s#{t_id}').src = image_path('save.png');alert('Can not find task #{t_id}');") and return if t == nil

    # responsible (user)
    uname = params['user']['name']
    u     = User.find_by_name(uname)
    render(:text=>"$('s#{t.id}').src='/images/save.png';alert('user #{uname} not found')") and return if u == nil
    if u.id != t.user_id
      # change state to inbox if we change the responsible
      t.propagate_state_change(t.state, 'inbox')
      t.state = 'inbox' 
      t.user_id    = u.id # actual user change
    end

    user = User.find(session['user'])


    # project
    pname = '' # used below
    if(params['project']) # nil happens when the project name field is disabled (because it is a subtask for example)
      pname = params['project']['name']
      if(pname != '')
        projects_by_name = Project.find_all_by_name(pname)
        if projects_by_name == []
          # not found, shall we create it ?
          if(params['C']=='1')
            p = Project.new(:name => pname)
            p.save
            p.users << user
            projects_by_name = [] << p
            # if this is the first project to this user, set it by default so the new task will have it by default
            session[:selected_project] = p.id if user.projects.size == 1
          else
            render(:text=>"create_project_confirm('#{t_id}', '#{item_name}');")
            return
          end
        end
        found = nil
        projects_by_name.each do |p|
          found = true and break if p.users.member?(u)
        end
        
        # in case we do not have permissions, render an error
        render(:text=>"$('s#{t.id}').src='/images/save.png';alert('This project already exists and you do not have permission to add tasks to it.');") and return if !found
        
        #if session[:selected_task] != '0'
          # so, a project name was sent to us.
          # but a task is selected, that only means that we just created a new task for this project
          # under the selected task. In that case, if we edited the name of the project
          # then change the parent to the root
          
          # well no need to do that since now the project name field is disabled:
          # we can not change the name of the project when we create a task under a selected task
        #end
        
        # check if the project was changed, if yes, edit subtasks' project name too
        t.propagate_project_change(p.id) if t.project_id != p.id
      end # pname not ''
    end  
    #t.update_attributes(params[:task])
    t.title      = params[:task][:title]
    t.priority   = params[:task][:priority]
        
    # done
    if(params[:task][:progress] != nil)
      done_has_changed = t.mark_done(params[:task][:progress])
    end
    
    # time estimation (must be done after 'done' is set)
    t.set_remains(params[:task][:orig_est],params[:task][:cur_est],params[:task][:elapsed])
    t.compute_remains # compute subremains and propagate to parents. save the task.
    
    # only the owner can change the task state
    state = params[:task][:state]
    if state != nil and t.state != state and t.user_id == session['user'].to_i
      t.propagate_state_change(t.state, state)
      t.state = state 
    end	

    t.project_id = p.id if(pname!='')# TODO: if the task's project changed, change all the subtask's project too
    t.duedate    = params[:task][:duedate]

    # tags
    t.taggings.destroy_all
    if t.title.include?(':')
      t.tag_list = t.title.split(':')[0].split(',').map {|tag| tag.strip.squeeze(" ")}.join(', ')
    end
    t.save

    @tasks,@tags,@project_id = select_tasks # so we have @tags

    tags      = esc_js(render_to_string(:partial=>"simple_tag", :collection => @tags))
    
    # by default we update the parent with subtasks
    # but if we selected a task we only update the edited task, not its parent
    #s = session[:selected_task].to_i
    #if(s != 0 and t.task_id == s)
      parent = t
      expand = false
    #else
    #  parent = t.task ? t.task : t
    #  expand = true
    #end
    txt = esc_js(render_to_string(:partial => 'task', :locals => {:task=>parent, :tasks=>@tasks, :expand=>expand, :item_name=>item_name}))
    #if(tags.size>1024 or txt.size>1024)
    #  txt = "<b>Error.</b> text too long. txt=#{txt.size}, tags=#{tags.size}. Please report this error (see email at the bottom of this page)."
    #  tags = ''
    #end
    render(:text => "saved('#{parent.id}','#{item_name}','#{txt}',#{t.done},#{done_has_changed});expand_tags('#{tags}','task','#{@project_id}');")
  end
  
  def undo_children
    id = params[:id]
    t = Task.find(id)
    t.done = 0
    t.progress = 0
    t.save
    t.mark_children_done(0)
    render(:partial => 'task', :locals => {:task=>t, :tasks=>t.tasks, :expand=>true, :item_name=>'task'})
  end
  
  def asking_delete
    id = params[:id]
    item_name = params[:item_name]
    begin
      t = Task.find(id)
      if t.tasks.size == 0
        # if the task has absolutely no subtasks, it is ok to delete it
        render(:text=>"del(#{id}, '#{item_name}');")
      else
        # render a popup displaying all subtasks
        @id = t.id
        @item_name = item_name
        txt = esc_js(render_to_string(:partial => 'confirm_delete', :locals => {:tasks=>t.tasks.sort_by{|t| [t.done,t.state]}}))
        render(:text=>"delete_confirm('#{txt}');")
      end
    rescue
      render(:text=>"alert('#{$!}');")
    end
  end
  
  # Ajaxed
  def delete
    id = params[:id].to_i
    return if not verify_task_rights(id)
    begin
      t = Task.find(id)
      # redundant with verify_task_rights
      if (t.project_id!=0 and not User.find(session['user']).projects.map {|p| p.id}.include?(t.project_id))
        render(:text=> '-1') # this task is assigned to a project but not to one of this user
        return
      end
      if Task.destroy(id)
        t.task.compute_remains if t.task # the parent estimation is recalculated
        render(:text=>"#{id}")
      else
        render(:text=>'-2')
      end
    rescue
      render(:text=>'-1')
    end
  end

  def select_project_index
    redirect_to(:controller=>'welcome', :action => 'index') if session['user'] == nil
    session[:selected_project] = params[:id]
    session[:selected_task] = '0'
    @tasks,@tags,@project_id = select_tasks
    redirect_to(:action => 'index')
  end

  # return a html string with the responsibles of this project
  def get_project_resp p
    return '' if(p == 0)
    p_resp = find_responsibles(p).map{|u| u.name }
    return "<div class='menu title'>members</div><div class='menu content'><ul class=\"mylist\"><li>#{p_resp.join("</li><li>")}</li></ul></div>"
  end

  #Ajaxed
  def show_project_resp
    render(:text => get_project_resp(params[:id].to_i))
  end

  #Ajaxed
  def select_project
    @project_id = params[:id].to_i
    session[:selected_project] = @project_id
    session[:selected_task] = '0' # display all tasks
    @tasks,@tags,@project_id = select_tasks
    #get_deadlines
    get_ost
    content = esc_js(render_to_string(:partial=>"taskcontent", :locals=>{:by_tag=>nil}))
    tags    = esc_js(render_to_string(:partial=>"simple_tag", :collection => @tags))
    resp    = esc_js(get_project_resp(params[:id].to_i))
    states  = esc_js(state_options)
    
    name = @project_id==0 ? "All":Project.find(@project_id).name
    render(:text => "$('selected_project').update('#{name}');display_tasks('#{content}');expand_tags('#{tags}','task','#{@project_id}');$('members').update('#{resp}');$('state_combo').update('#{states}')")
  end

  def get_state_count
    tasks, t,@project_id = select_tasks('','all')
    state_count = Hash.new(0)
    state_count['all'] = tasks.select{|t| t.task_id==0}.size
    ['inbox','next','waiting','maybe','ref'].each {|s| 
      state_count[s] = tasks.select{|t| t.state==s and t.task_id==0}.size
      }
    state_count
  end

  def update_resp
    render(:partial => "respcontent")
  end

  #Ajaxed
  def select_resp
    u = User.find(session['user'])
    session[:selected_resp] = params[:id]
    u.settings.default_resp = params[:id]
    u.save


    @tasks,@tags, @project_id = select_tasks # TODO: tags ?
    #get_deadlines
    get_ost
    content = esc_js(render_to_string(:partial=>"taskcontent", :locals=>{:by_tag=>nil}))
    tags    = esc_js(render_to_string(:partial=>"simple_tag", :collection => @tags))
    states  = esc_js(state_options)
    render(:text => "display_tasks('#{content}');expand_tags('#{tags}','task','#{@project_id}');$('state_combo').update('#{states}')")
  end

  def select_state
    u = User.find(session['user'])
    session[:selected_state] = params[:id]
    u.settings.default_state = params[:id]
    u.save
    display_tasks
  end

  def select_special # magic is in select_task
    u = User.find(session['user'])
    session[:selected_special] = params[:id]
    u.settings.default_special = params[:id]
    u.save
    display_tasks
  end

  def display_tasks
    @tasks,@tags, @project_id = select_tasks
    #get_deadlines
    get_ost
    content = esc_js(render_to_string(:partial=>"taskcontent", :locals=>{:by_tag=>nil}))
    tags    = esc_js(render_to_string(:partial=>"simple_tag", :collection => @tags))
    render(:text => "display_tasks('#{content}');expand_tags('#{tags}','task','#{@project_id}');")
  end
  
  #Ajaxed
  def order
    i = 1
    params[:taskslist].each { |id|
      Task.update(id, :order => i)
      i = i+1
    }
    render(:nothing => true)
  end

  def create_project
    render(:layout => false)
  end

  def export_csv
    @tasks,@tags,@project_id = select_tasks
    str = "Order,Project,Priority,Title,Responsible,Due date\n"
    @tasks.each { |t|
      da = t.duedate.to_s
      str += '"'+t.order.to_s+'","'+t.project.name.to_s+'","'+t.priority.to_s+'","'+t.title.to_s.gsub("\n",' ')+'","'+t.user.name.to_s+'","'+ da +"\"\n"
    }
    response.headers['Content-Type'] = 'text/csv'
    response.headers['Content-Disposition'] = 'attachment; filename=export.csv'
    render(:text => str)
  end

  def export_freemind
    response.headers['Content-Type'] = 'application/mm'
    response.headers['Content-Disposition'] = 'attachment; filename=export.mm'
    str = "<map version=\"0.8.1\">\n<node ID=\"root\" TEXT=\"tasks\">\n"
    @tasks,@tags,@project_id = select_tasks
    id = 1
    make_tree_from_project.each { |tag,tasks|
      str += "\n<node ID=\"tag_#{id}\" TEXT=\"#{tag}\">\n"
      tid = 1
      tasks.each { |task|
        str += "<node ID=\"#{id}_#{tid}\" TEXT=\"#{task}\"/>\n"
        tid += 1
      }
      str += "</node>\n"
      id += 1
    }
    str += "</node>\n</map>\n"
    render(:text => str)
  end

  # attach this task to his sibling
  # TODO: it's not a good visually way to do it.....
  def demote
    list = params[:taskslist]
    id = params[:id]
    index = list.index(id)
    if index and index > 0
      t = Task.find(id)
      parent = Task.find(list[index-1])
      t.task_id = parent.id if parent
      t.save
    end  
    #render(:text=>"t=#{t.id}, parent=#{parent.id}")
    @tasks,@tags,@project_id = select_tasks
    render(:partial => "task", :object => @task, :collection => @tasks, :order=>"`order`")
  end

  #Ajaxed
  def sort_by_priority
    tags  =  params[:t]
    @tasks,@tags,@project_id = select_tasks(tags)
    # sort
    @tasks = sort_tasks
    i = 1
    @tasks.each { |t|
      Task.update(t.id, :order => i)
      i = i+1
      }
    get_ost
    render(:partial=>'taskcontent')
  end

  def sort_by_estimation
    #tags  =  params[:t]
    @tasks,@tags,@project_id = select_tasks()#tags)
    # sort
    @tasks = sort_tasks_by_estimation
    i = 1
    @tasks.each { |t|
      Task.update(t.id, :order => i)
      i = i+1
      }
    get_ost
    render(:partial=>'taskcontent')
  end


  #Ajaxed
  def star_it
    begin
      t = Task.find(params[:id])
      case t.star
      when 0
        t.update_attribute(:star,1)
      when 1
        t.update_attribute(:star,0)
      end
      render(:text=>"star(#{t.id},#{t.star})")
    rescue Exception => e
      render(:text=>"alert(\"#{e.message}\")")
    end
  end
  
  def auto_complete_for_user_name
    re = Regexp.new("#{params[:user][:name]}", "i")
    p = (session[:selected_project] || 0).to_i
    find_responsibles(p)
    @users = @resp.collect(&:name).select { |u| re.match u }.sort
    render :inline => "<%= content_tag(:ul, @users.map { |u| content_tag(:li, h(u)) }) %>"
  end

  def auto_complete_for_project_name
    re = Regexp.new("#{params[:project][:name]}", "i")
    p_ids = User.find(session['user']).active_projects_ids.join(',')
    render :nothing=>true and return if p_ids == ''
    @p = Project.find(:all,:conditions=>"id in (#{p_ids})").collect(&:name).select { |u| re.match u }.sort
    render :inline => "<%= content_tag(:ul, @p.map { |u| content_tag(:li, h(u)) }) %>"
  end

  def state_options
    state_count = get_state_count
    rv = "<option value='0'>All (#{state_count['all']})</option>"
    STATES.each { |text, state|
      rv += "<option value='#{state}' "
      rv += " selected" if session[:selected_state]==state
      rv += ">#{text} (#{state_count[state]})</option>"
      }
    rv  
  end
  
  def cut
    id = params['id']
    item_name = params['item_name']
    session['cut'] = id
    render :text => "new Effect.Highlight($('#{item_name}_#{id}'))"
  end
  
  def paste
    id        = params['id'].to_i
    item_name = params['item_name']
    cut_id    = session['cut'].to_i
    render(:text=>'alert("Can not paste on itself")') and return if id == cut_id
    begin
      cut_item  = Task.find(cut_id, :include=>"task")
      from_item = cut_item.task
      from_item = nil if from_item and from_item.id == session[:selected_task].to_i
      if id != 0 # if 0 means paste to root
        to_item   = Task.find(id)
      else
        to_item = nil
      end
    rescue
      render(:text=>'alert("No task to paste (cut before you paste)")')
      return
    end
    render(:text=>"alert('Can not paste on a descendant')") and return if to_item and to_item.ancestors.include?(cut_item)
    
    cut_item.task_id = id # this is the actual paste
    cut_item.project_id = to_item.project_id if to_item
    cut_item.save
    
    # time estimation: update of cut old parent, and cut new parent
    to_item.compute_remains    if to_item
    from_item.compute_remains  if from_item

    tasks, tags,@project_id = select_tasks # display taking into account the state
    
    if id == 0 or session[:selected_task].to_i==id # paste to root or into sel_task root
      to_text   = esc_js(render_to_string(:partial => "task", :locals=>{:task=>cut_item, :tasks=>tasks, :expand=>false, :item_name=>item_name}))
    else
      to_text   = esc_js(render_to_string(:partial => "task", :locals=>{:task=>to_item, :tasks=>tasks, :expand=>true, :item_name=>item_name}))
    end
    from_text = (from_item ? esc_js(render_to_string(:partial => "task", :locals=>{:task=>from_item, :tasks=>tasks, :expand=>true, :item_name=>item_name})):'')
    to_id     = (to_item   ? id : '')
    from_id   = (from_item ? from_item.id : cut_id) # from is the parent, so if no parent, take the node itself
    
    sel_task = ((session[:selected_task]!='0') and session[:selected_task].to_i==id) ? 1 : 0
    if id == 0 # paste to root
      render :text => "paste_anim('#{item_name}_#{from_id}','','#{from_text}','#{to_text}','#{item_name}_#{cut_id}',#{sel_task});"
    else
      render :text => "paste_anim('#{item_name}_#{from_id}','#{item_name}_#{to_id}','#{from_text}','#{to_text}','#{item_name}_#{cut_id}',#{sel_task});"
    end
  end
  
  def display_tasks_search_results
    title = params['task']['title'].gsub(/\\/, '\&\&').gsub(/'/, "''") 
    #re = Regexp.new("#{title}", "i")
    user = User.find(session['user'])
    p_ids = user.active_projects_ids.join(',')
    @search = Task.find(:all,:conditions=>"title like '%#{title}%' and (project_id in (#{p_ids}) or user_id=#{user.id})", :order=>"done, project_id, state")
    if @search.size > 0
      render :inline => "<ul><%= render(:partial=>'task', :collection=>@search, :locals=>{:tasks=>[], :item_name=>'s'}) %></ul>"
    else
      render :text => "No results."
    end
    #render :text => "#{title}"
  end
  
  def show_other_states
    sel_task = params['id'].to_i
    sel_task = session[:selected_task] if sel_task == 0
    state = session[:selected_state]
    tasks = sort_tasks(select_tasks('',"!#{state}","#{sel_task}")[0])
    render(:partial=>'task', :collection=>tasks, :locals=>{:tasks=>[], :item_name=>'item'})
  end
  
  def close
    id = params[:id]
    task = Task.find(id)
    render(:partial=>'task_c', :object=>task, :locals=>{:tasks=>[], :item_name=>'item', :expand=>false})
  end

  def print
    id = params[:id]
    if id != '0' and id != nil
      verify_task_rights(id)
      @task = Task.find(id)
      if @task.project
        @project_name = @task.project.name
      else
        @project_name = "No project"  
      end  
    else
      sel_task_id   = session[:selected_task].to_i
      @task         = sel_task_id != 0 ? Task.find(sel_task_id) : nil
      @tasks        = select_tasks[0].select { |t| t.task_id == sel_task_id} # FIXME : Arrrrg !!!
      @project_name = Project.find(session[:selected_project]).name
     end  
    render(:layout=>false)
  end

  def quick_done
    id       =  params[:id]
    progress = (params[:d].to_i==1 ? 2 : 0)
    t = Task.find(id)
    t.mark_done(progress)
    t.save
    render(:nothing=>true)
  end

  #Ajaxed
  def refresh_task_content
    @tasks,@tags,@project_id = select_tasks
    get_ost
    render(:partial=>'taskcontent')
  end

  def suggest
    time  = params[:t]
    cond  = "user_id=#{current_user.id} and project_id='#{session[:selected_project]}' and done=0 and subremains > 0 and subremains <= #{time}"
    @tasks =  Task.find(:all, :conditions=>cond)
    @tasks =  @tasks.sort_by { |t|
      d = t.duedate || Date.civil(2030,1,1)
      p = t.priority || 999
      [-t.progress, d, p, -t.star, -t.subremains]
      }.first(15)

    render(:partial=>'taskcontent', :locals=>{:by_tag=>nil})
  end
  
private

  def sanitize(txt)
    HTMLEntities::encode_entities(Iconv.new('US-ASCII//TRANSLIT', 'utf-8').iconv(txt))
  end
  
  # used by export_freemind
  def make_tree_from_project
    tags = Hash.new { |hash, key| hash[key] = Array.new}
    @tasks.each {|t|
      if(t.tags.size==0)
        tags['not tagged'] << sanitize(t.title)
      else
        t.tags.each { |tag|
          tags[tag] << sanitize(t.title)
        }
      end
    }
    tags
  end

  def verify_task_rights(task_id)
    r = current_user.check_task_rights(task_id)        
    render(:text=>'401 - Unauthorized', :status=>:unauthorized) if not r
    r
  end

  def sort_tasks(tasks=@tasks)
    tasks.sort_by { |t|
      d = t.duedate || Date.civil(2030,1,1)
      p = t.priority || 999
      [-t.progress, d, p, -t.star, t.state, t.order]
      }
  end

  def sort_tasks_by_estimation(tasks=@tasks)
    tasks.sort_by { |t|
      r = t.subremains || 999
      r = 999 if r == 0
      [r, t.order]
      }
  end

  
end

